﻿using System;
using System.IO;
using System.Text;

namespace Truncation.Detector.Windows
{
    public class FileEncodingDetector
    {
        public static Encoding DetectFileEncoding(string fileName)
        {
            using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                return DetectFileEncoding(stream);
            }
        }

        public static Encoding DetectFileEncoding(FileStream stream)
        {
            Encoding found = null;
            var savedPos = stream.Position;
            stream.Position = 0;
            var bomBytes = new byte[stream.Length > 4 ? 4 : stream.Length];
            stream.Read(bomBytes, 0, bomBytes.Length);
            stream.Position = savedPos;
            found = DetectBomBytes(bomBytes);
            return found ?? Encoding.GetEncoding(1252);
        }
        private static Encoding DetectBomBytes(byte[] bomBytes)
        {
            if (bomBytes == null)
                throw new ArgumentNullException("Must provide a valid BOM byte array!", "BOMBytes");

            if (bomBytes.Length < 2)
                return null;

            if (bomBytes[0] == 0xff
                && bomBytes[1] == 0xfe
                && (bomBytes.Length < 4
                    || bomBytes[2] != 0
                    || bomBytes[3] != 0
                    )
                )
                return Encoding.Unicode;

            if (bomBytes[0] == 0xfe
                && bomBytes[1] == 0xff
                )
                return Encoding.BigEndianUnicode;

            if (bomBytes.Length < 3)
                return null;

            if (bomBytes[0] == 0xef && bomBytes[1] == 0xbb && bomBytes[2] == 0xbf)
                return Encoding.UTF8;

            if (bomBytes[0] == 0x2b && bomBytes[1] == 0x2f && bomBytes[2] == 0x76)
                return Encoding.UTF7;

            if (bomBytes.Length < 4)
                return null;

            if (bomBytes[0] == 0xff && bomBytes[1] == 0xfe && bomBytes[2] == 0 && bomBytes[3] == 0)
                return Encoding.UTF32;

            if (bomBytes[0] == 0 && bomBytes[1] == 0 && bomBytes[2] == 0xfe && bomBytes[3] == 0xff)
                return Encoding.GetEncoding(12001);

            return null;
        }

    }
}
